package com.wenyl.interview.redis.service.impl;

import com.wenyl.interview.redis.service.IUserService;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
/**
 * @author Swimming Dragon
 * @description: TODO
 * @date 2024年09月25日 16:48
 */
@Service
public class UserServiceImpl implements IUserService {
    private static final String LOCK_KEY = "userLock";
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private RedisTemplate<String,String> redisTemplate;
    @Autowired
    private RBloomFilter<String> userInfoBloomFilter;

    @Override
    public String getCacheDataById(String userId) {
        String cachedUserInfo = redisTemplate.opsForValue().get(userId);
        if(!StringUtils.hasLength(cachedUserInfo)){
            // 缓存没有数据
            RLock rLock = redissonClient.getLock(LOCK_KEY);
            try{
                rLock.lock();
                // 模拟从数据库加载
                Thread.sleep(500);
                String userInfo = "用户信息";
                redisTemplate.opsForValue().set(userId,userInfo);
                return userInfo;
            }catch (InterruptedException e){
                throw new RuntimeException("线程休眠异常");
            }finally {
                rLock.unlock();
            }
        }
        return cachedUserInfo;
    }

    @Override
    public String getById(String userId) {
        try {
            // 先从缓存中查询用户
            String cachedUserInfo = redisTemplate.opsForValue().get(userId);
            if(cachedUserInfo == null){
                System.out.println("redis中没有查询到数据，进入数据库查询");
                // 模拟数据库查询用户,数据库中也没有这个用户，返回null
                Thread.sleep(100);
                return null;
            }
            return cachedUserInfo;

        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
    @Override
    public String getByIdv2(String userId) {
        try {
            // 先从缓存中查询用户
            String cachedUserInfo = redisTemplate.opsForValue().get(userId);
            if(cachedUserInfo == null){
                System.out.println("redis中没有查询到数据，进入数据库查询");
                // 模拟数据库查询用户,数据库中也没有这个用户，返回null，将null值设置在redis
                Thread.sleep(100);
                redisTemplate.opsForValue().set(userId,"null");
                return null;
            }
            return cachedUserInfo;

        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public String getByIdv3(String userId) {
        try {
            boolean exist = userInfoBloomFilter.contains(userId);
            // 过滤器中不存在这个用户，查询数据库，有的话将数据存入redis，没有就在redis缓存一个null
            if(!exist){
                // 这里从redis查询是因为布隆过滤器为空时，也把null值缓存到了redis
                String cachedUserInfo = redisTemplate.opsForValue().get(userId);
                // redis还没有设置过null值
                if(cachedUserInfo == null){
                    System.out.println("布隆过滤器不存在数据，查询数据库是否有数据并缓存");
                    /*
                     * 正常这里应该还要判断数据库返回值是否为null，不为null则缓存数据到redis，同时把数据添加到过滤器，
                     * 若为null则把null缓存到redis
                     * 方便演示我直接缓存一个null作为用户信息
                     * */
                    Thread.sleep(100);
                    redisTemplate.opsForValue().set(userId,"null值");
                    return "null值";
                }else{
                    return cachedUserInfo;
                }
            }else{
                /*
                 * 过滤器存判断存在则查询缓存中的数据，这里要注意因为布隆过滤器可能存在误报
                 * 所以在判断一次缓存值是否存在并进行对应操作
                 * */
                String cachedUserInfo = redisTemplate.opsForValue().get(userId);
                if(cachedUserInfo == null){
                    System.out.println("布隆过滤器误报，查询数据库");
                    Thread.sleep(100);
                    // 如果有则返回并设置缓存，更新布隆过滤器，没有就设置缓存为null
                    redisTemplate.opsForValue().set(userId,"null值");
                    return "null值";
                }
                return cachedUserInfo;
            }

        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

}
